package edu.stanford.bmir.protege.web.server.stkosutil.cutter;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Stack;

import org.protege.editor.owl.model.hierarchy.OWLDataPropertyHierarchyProvider;
import org.semanticweb.owlapi.model.AddAxiom;
import org.semanticweb.owlapi.model.EntityType;
import org.semanticweb.owlapi.model.OWLAnnotation;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLDataProperty;
import org.semanticweb.owlapi.model.OWLLiteral;
import org.semanticweb.owlapi.model.OWLOntologyChange;
import org.semanticweb.owlapi.model.RemoveAxiom;

import edu.stanford.bmir.protege.web.client.rpc.stkosutil.common.ItemSelector;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.common.MatchLevel;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.common.UtilConstants;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.CuttingMode;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.CuttingOptions;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.CuttingResult;
import edu.stanford.bmir.protege.web.client.rpc.stkosutil.cutter.CuttingScope;
import edu.stanford.bmir.protege.web.server.owlapi.OWLAPIProject;
import edu.stanford.bmir.protege.web.server.owlapi.OWLAPIProjectManager;
import edu.stanford.bmir.protege.web.server.owlapi.RenderingManager;
import edu.stanford.bmir.protege.web.server.stkosutil.mapper.OwlPropertyHolder;
import edu.stanford.bmir.protege.web.shared.project.ProjectId;
import edu.stanford.bmir.protege.web.shared.user.UserId;


public class DataPropertyCutter {
			
		/****
		*The following functions are copied from OntologyServiceOWLAPIImpl as they are not visible
		* 
		*/
	private OWLAPIProject getProject(String projectName) {
	   if (projectName == null) {
		   throw new NullPointerException("projectName must not be null");
		}
		ProjectId projectId = ProjectId.get(projectName);
		// TODO: Log
		return getProject(projectId);
	}
	/**
	     * Gets the OWLAPIProject for a given {@link ProjectId}.If a project with the specified id exists then that project
	     * will be returned, otherwise, a fresh project will be created and that fresh project returned.
	     * @param projectId The id of the project.
	     * @return The OWL API project. Not <code>null</code>.
	 */
	private OWLAPIProject getProject(ProjectId projectId) {
	     OWLAPIProjectManager pm = OWLAPIProjectManager.getProjectManager();
		 return pm.getProject(projectId);
	}
	//*******************   
	private List<OwlPropertyHolder> getSubProperties(OWLAPIProject project, String propertyName) {
	       if (propertyName == null) {
	           return Collections.emptyList();
	       }
		   List<OwlPropertyHolder> result = new ArrayList<OwlPropertyHolder>();
		   RenderingManager rm = project.getRenderingManager();
		   OWLDataPropertyHierarchyProvider hierarchyProvider = project.getDataPropertyHierarchyProvider();
		   OWLDataProperty property = rm.getEntity(propertyName, EntityType.DATA_PROPERTY);
			        
		   for (OWLDataProperty subProperty : new ArrayList<OWLDataProperty>(hierarchyProvider.getChildren(property))) {
			   String browserText = rm.getBrowserText(subProperty);
			   String name = subProperty.getIRI().toString();
			   OwlPropertyHolder propertyHolder = new OwlPropertyHolder();;
			   propertyHolder.setBrowserText(browserText);
			   propertyHolder.setName(name);
			   propertyHolder.setOwlDataProperty(subProperty);
			   result.add(propertyHolder);

          }
		  Collections.sort(result, new Comparator<OwlPropertyHolder>() {
			  public int compare(OwlPropertyHolder o1, OwlPropertyHolder o2) {
			      String browserText1 = o1.getBrowserText();
			      String browserText2 = o2.getBrowserText();
			      if(browserText1.startsWith("'")) {
			             browserText1 = browserText1.substring(1);
			      }
			      if(browserText2.startsWith("'")) {
			            browserText2 = browserText2.substring(1);
			      }
			      return browserText1.compareToIgnoreCase(browserText2);
			    }
			});
			return result;
	}	
			    
				
	private List<OwlPropertyHolder> loadAllPropertyList(OWLAPIProject project, String topPropertyName){
	 	List<OwlPropertyHolder> result = new ArrayList<OwlPropertyHolder>();
	 	Stack<OwlPropertyHolder> sPropertyHolders = new Stack<OwlPropertyHolder>();
	 	OwlPropertyHolder root = this.getTopProperty(project, topPropertyName);
	 	sPropertyHolders.push(root);
	 	while (!sPropertyHolders.empty()){
		 		OwlPropertyHolder currentProperty = sPropertyHolders.pop();
		 		List<OwlPropertyHolder> children = this.getSubProperties(project, currentProperty.getName());
			 	Collections.reverse(children);
			 	for (OwlPropertyHolder child: children){
			 		sPropertyHolders.push(child);
			 	}
			 	result.add(currentProperty);		 
		}
		return result;
	}
			 
	private OwlPropertyHolder getTopProperty(OWLAPIProject project, String topPropertyName) {
		OwlPropertyHolder holder = new OwlPropertyHolder();
		RenderingManager rm = project.getRenderingManager();
		OWLDataProperty topProperty = null;
		if (topPropertyName==null){
			topProperty = project.getDataFactory().getOWLTopDataProperty();
		}else{
			topProperty = rm.getEntity(topPropertyName, EntityType.DATA_PROPERTY);
		}
		String browserText = rm.getBrowserText(topProperty);
		String name = topProperty.getIRI().toString();
		holder.setBrowserText(browserText);
		holder.setName(name);
		holder.setOwlDataProperty(topProperty);
		return holder;
	}
	public CuttingResult cutOntology(String projectName, CuttingOptions options, UserId userId){
	 	String refProjectName = options.getRefProjectName();
		CuttingResult result = new CuttingResult();
		List<OwlPropertyHolder> oPropertyList = null;
			 	
		List<OWLOntologyChange> oChangeList = new ArrayList<OWLOntologyChange>();
			 		
		OWLAPIProject currProject = this.getProject(projectName);
		//OWLAPIProject refProject = this.getProject(refProjectName);
		String topPropertyName = null;
		if (options.getCuttingScope()==CuttingScope.SELECTED_PROPERTY_TREE){
			topPropertyName = options.getSelectedEntityName();
		}
			 		
		oPropertyList= this.loadAllPropertyList(currProject, topPropertyName);
		int cutNumber = this.fillCuttingFlagAxiom(currProject, oPropertyList, oChangeList,options);
		currProject.applyChanges(userId, oChangeList, "本体自动裁切");
		result.setCutDataPropertyNumber(cutNumber);
		result.setTotalDataPropertyNumber(oPropertyList.size());
		return result;
	}
	private int fillCuttingFlagAxiom(OWLAPIProject project, List<OwlPropertyHolder> propertyList, List<OWLOntologyChange> changeList, CuttingOptions options){
		int count = 0;
		OWLDataFactory df = project.getDataFactory();
		for (OwlPropertyHolder owlPropertyHolder: propertyList){
			 OWLDataProperty owlProperty = owlPropertyHolder.getOwlDataProperty(); 
			 boolean hasMappingNo=false;
			 			
			 for (OWLAnnotation annotation : owlProperty.getAnnotations(project.getRootOntology(), df.getRDFSComment())) {
				  if (annotation.getValue() instanceof OWLLiteral) {
					  OWLLiteral commentValue = (OWLLiteral) annotation.getValue();
					  String value = commentValue.getLiteral();
					  if (value.startsWith(UtilConstants.FLAG_MAPPING_NO)){
						  hasMappingNo = true;
					  }
					  if (value.startsWith(UtilConstants.FLAG_CUTTING)){			  
						  OWLAxiom oldAx = df.getOWLAnnotationAssertionAxiom(
					      owlProperty.getIRI(), annotation);
						  RemoveAxiom removeAxiom = new RemoveAxiom(project.getRootOntology(),oldAx);
						  changeList.add(removeAxiom);
					   }	    			  				    		  }
			}//end for
			 			
			if (qualifiedByCondition(owlPropertyHolder, hasMappingNo, options)){
			 	String annotaionValue =  UtilConstants.FLAG_CUTTING + ": " + "Y"; 
			 	OWLAnnotation commentAnno = df.getOWLAnnotation(
				df.getRDFSComment(),
				df.getOWLLiteral(annotaionValue, "en"));
				OWLAxiom ax = df.getOWLAnnotationAssertionAxiom(
				owlProperty.getIRI(), commentAnno);
				AddAxiom addAxiom =  new AddAxiom(project.getRootOntology(), ax);	 
				changeList.add(addAxiom);
				count++;
			 }
			 			
		}//end for
		return count;	 			
	}
	private boolean qualifiedByCondition(OwlPropertyHolder propertyHolder, boolean hasMappingNo, CuttingOptions options){
			if (options.getCuttingMode()==CuttingMode.REF_PROJECT){
				boolean qualified =  (hasMappingNo && options.getItemSelector()==ItemSelector.MATCHED_ITEMS)
						|| (hasMappingNo==false && options.getItemSelector()==ItemSelector.NOT_MATCHED_ITEMS);
			 	return qualified;
			 }else{
			 	boolean matched = false;
			 	String search = options.getSearch();
			 	String entityName = propertyHolder.getBrowserText();
			 	if (search.compareTo("*")==0){
			 		return options.getItemSelector()==ItemSelector.MATCHED_ITEMS;
			 	}
			 	if (options.getMatchLevel()==MatchLevel.CASE_SENSITIVE){
			 		matched = entityName.compareTo(search)==0;
			 	}else if (options.getMatchLevel()==MatchLevel.CASE_INSENSITIVE){
			 		matched = entityName.compareToIgnoreCase(search)==0;
			 	}else if (options.getMatchLevel()==MatchLevel.INCLUSIVE){
			 		search = search.toLowerCase();
			 		entityName = entityName.toLowerCase();
			 		if (entityName.indexOf(search)!=-1){
			 			matched=true;
			 		}else{
			 			matched=false;
			 		}
			 	}
			 	if (options.getItemSelector()==ItemSelector.MATCHED_ITEMS){
			 		return matched;
			 	}else{
			 		return !matched;
			 	}
			 }
		}
	}
